﻿using System;
using System.Collections.Generic;
using System.Text;

namespace HR.Appointments.Service.ValueObjects
{
    /// <summary>
    /// Class to compare and hold time units for the system.
    /// </summary>
    public class TimeUnit : ICloneable, IComparable
    {
        #region Private Memebers
        private int _hour;
        private int _minute;
        private bool _isNextDay = false;
        #endregion

        #region Constructors
        private TimeUnit(int hour, int minute)
        {
            if(hour > 23 || hour < 0)
                throw new ArgumentOutOfRangeException("hour");

            if(minute > 59 || minute < 0)
                throw new ArgumentOutOfRangeException("minute");

            _hour = hour;
            _minute = minute;
        }
        #endregion

        #region Properties
        /// <summary>
        /// The timeunits hour
        /// </summary>
        public int Hour
        {
            get
            {
                return _hour;
            }
            set
            {
                _hour = value;
            }
        }

        /// <summary>
        /// The timeunits minutes
        /// </summary>
        public int Minute
        {
            get
            {
                return _minute;
            }
            set
            {
                _minute = value;
            }
        }

        /// <summary>
        /// After adding did we switch to a new day
        /// </summary>
        public bool IsNextDay
        {
            get
            {
                return _isNextDay;
            }
            set
            {
                _isNextDay = value;
            }
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Add a duration to the timeunit
        /// </summary>
        /// <param name="minutes">Duration in minutes</param>
        public TimeUnit Add(int minutes)
        {
            //don't add minutes that is more than a hole day.
            if (minutes > ((60*24)-1) || minutes < 0)
                throw new ArgumentOutOfRangeException("minute");

            _minute += minutes % 60;
            _hour += minutes / 60;

            if (_minute > 59)
            {
                _hour += 1;
                _minute -= 60;
            }

            if (_hour > 23)
            {
                _isNextDay = true;
                _hour -= 24;
            }

            return (TimeUnit)this.Clone();
        }
        #endregion

        #region ICloneable Member
        /// <summary>
        /// Creata a new instance of the timeunie.
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            TimeUnit t = new TimeUnit(_hour, _minute);
            return t;
        }
        #endregion

        #region IComparable Member
        /// <summary>
        /// Compares to object to be of same value.
        /// </summary>
        /// <param name="obj">Object to company</param>
        /// <returns>True if both object have the same values</returns>
        public int CompareTo(object obj)
        {
            if (obj is TimeUnit)
            {
                TimeUnit tu = (TimeUnit)obj;
                if (tu.Hour == Hour)
                {
                    return _minute.CompareTo(tu.Minute);
                }
                else if (Hour > tu.Hour)
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }
            else
            {
                throw new ArgumentException("Object is not a TimeUnit.");
            }
        }
        #endregion

        #region Override Methods
        /// <summary>
        /// Determines whether the specified Object is equal to the current Object.
        /// </summary>
        /// <param name="obj">The Object to compare with the current Object.</param>
        /// <returns>true if the specified Object is equal to the current Object; otherwise, false.</returns>
        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType()) return false;
            TimeUnit eql = (TimeUnit)obj;
            return ((eql.Hour == Hour) && (eql.Minute == Minute));
        }

        /// <summary>
        /// Serves as a hash function for a this type.
        /// </summary>
        /// <returns>A hash code for the current Object.</returns>
        public override int GetHashCode()
        {
            return _hour.GetHashCode() ^ _minute.GetHashCode();
        }

        /// <summary>
        /// Returns a String that represents the current Object.
        /// </summary>
        /// <returns>A String that represents the current Object.</returns>
        public override string ToString()
        {
            return _hour.ToString().PadLeft(2, '0') + ":" + _minute.ToString().PadLeft(2, '0');
        }
        #endregion

        #region Static Methods
        /// <summary>
        /// Create a new time unit.
        /// </summary>
        /// <param name="hour">Hour of the timeunit</param>
        /// <param name="minute">Minute of the timeunit</param>
        /// <returns>A new timeunit</returns>
        public static TimeUnit Create(int hour, int minute)
        {
            return new TimeUnit(hour, minute);
        }

        /// <summary>
        /// Create new time unit from string
        /// </summary>
        /// <param name="time">Allowed format is hh:mm</param>
        /// <returns>A new timeunit</returns>
        public static TimeUnit Create(string time)
        {
            string[] split = time.Split(':');
            System.Diagnostics.Debug.Assert(split.Length == 2, "Incorrect format for TimeUnit");

            return new TimeUnit(Convert.ToInt32(split[0]), Convert.ToInt32(split[1]));
        }

        #endregion
    }
}
